#pragma once
#include "../TCP/TCPClient.h"
#include "../UDP/UDPClient.h"
#include <vector>
#include "ikcp.h"

class KCPClient : public Dispatcher
{
public:
	KCPClient();
	~KCPClient();

	void Disconnect();

	void AsyncConnect(const char* ip, int port, int timeout = -1);

	void Update();

	bool SetServerClient(TCPClient* serverClient);

	void OnTCPReceived(char* data, int len);

	int Send(char* data, int size);

	bool GetRemoteAddr(sockaddr_in* out);

	bool GetLocalAddr(sockaddr_in* out);

	void SetRemoteAddr(sockaddr_in* in);

	void SetLocalAddr(sockaddr_in* in);

	template <class _Fx, class _Types>
	void RegisterConnectCompletedCallback(_Fx&& _Func, _Types&& _User) {
		onConnectCompleted = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2);
	}

	template <class _Fx>
	void RegisterConnectCompletedCallback(_Fx&& _Func) {
		onConnectCompleted = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1);
	}

	void UnregisterConnectCompletedCallback() {
		onConnectCompleted = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterDisconnectedCallback(_Fx&& _Func, _Types&& _User) {
		onDisconnectedEvent = std::bind(_STD forward<_Fx>(_Func), _User);
	}

	template <class _Fx>
	void RegisterDisconnectedCallback(_Fx&& _Func) {
		onDisconnectedEvent = std::bind(_STD forward<_Fx>(_Func));
	}

	void UnregisterDisconnectedCallback() {
		onDisconnectedEvent = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterReceivedCallback(_Fx&& _Func, _Types&& _User) {
		onReceivedEvent = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2);
	}

	template <class _Fx>
	void RegisterReceivedCallback(_Fx&& _Func) {
		onReceivedEvent = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2);
	}

	void UnregisterReceivedCallback() {
		onReceivedEvent = nullptr;
	}

public:

	void* user = nullptr;

private:

	void OnTCPConnectCompleted(int code, std::string error);

	void OnTCPDisconnected();

	void OnUDPReceived(char* buffer, int len, sockaddr_in* remote);

	int KCPOutput(const char* buf, int len, ikcpcb* kcp);

	static int kcp_output(const char* buf, int len,ikcpcb* kcp, void* user);

	void ResetKCP();

private:

	std::function<void(int, std::string)> onConnectCompleted;
	std::function<void()> onDisconnectedEvent;
	std::function<void(char* data, int size)> onReceivedEvent;

private:


	TCPClient* tcpClient = nullptr;
	TCPClient* serverClient = nullptr;
	UDPClient* udpClient = nullptr;

	int clientUDPPort = -1;
	int serverUDPPort = -1;
	int state = 0;
	std::vector<char> connectBuffer;
	ikcpcb* kcp = nullptr;
	char remoteAddr[32];
	char localAddr[32];
};

